﻿// 线程.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <stdio.h>
#include <Windows.h>

/*
同时做多件事情:多线程
	服务器：N+1件事
		循环accept等待客户端连接
		如果有N个客户端连接上服务器
			N个死循环	循环去和客户端交互
	客户端：2件事同时做
		循环等待用户输入并存储并发送到服务器
		循环等待服务器发来数据并存储并显示



	创建线程函数：
		CreateThread

	子线程	和	主线程（入口函数）并行


	一个进程中的所有线程共享进程的所有资源	所以	传递数据给线程函数	就是函数传参

	线程函数有且只有一个参数		类型为void*


	等待指定对象处于信号状态或超时间隔已过:
		WaitForSingleObject
	若要等待多个对象，请使用:
		WaitForMultipleObjects


	结束掉当前进程：exit(-2)
		GetExitCodeThread		线程的		检索指定线程的终止状态
		GetExitCodeProcess		进程的


	线程结束：
		1.函数返回了
		2.主线程结束了
			主线程自然结束	main函数return了
			主线程非自然结束	exit
			被别的程序干掉了	TerminateProcess
		3.被其他线程干掉
			TerminateThread


	多个线程同时访问临界数据，导致临界数据脏：
	
	我们通常把解决  多个线程同时访问一块区域的这种问题 称为 线程同步：
		核心思想： 都是把时间错开

		用户态：api层面就解决掉问题，不需要调用内核代码
			用户态的解决方案：
				原子锁：
					最小的 叫 原子 （不可分割）
					把某个行为 设置为原子行为  （不可分割）
			
					字节 最小管理单位 

					原子操作的函数：
						InterlockedIncrement   ++

					volatile  关键字 
					让编译器不优化 

						int n = 666;

						int* p = &n;

						*p == 666


					//物理寄存器的地址
					#define  Addr     volatile *(int*)0xabcdfafa
					Addr = 123;    


				读写锁：
					腾讯文档 
						读 和 读  互相不影响
						读读相容 读写相斥

					1. 创建读写锁   SRWLock
							定义读写锁变量
					2. 初始化  InitializeSRWLock
					3. 使用
						3.1 加锁  使用前 加锁
							3.1.1 加读锁  AcquireSRWLockShared
							3.1.2 加写锁  AcquireSRWLockExclusive
						3.2 解锁  使用后 解锁
							3.2.1 解读锁  ReleaseSRWLockShared
							3.2.2 解写锁  ReleaseSRWLockExclusive

				临界区：
					1. 定义临界区   CRITICAL_SECTION
					2. 初始化临界区 InitializeCriticalSection
					3. 进入临界区   EnterCriticalSection
					4. 离开临界区   LeaveCriticalSection
					5. 删除临界区   DeleteCriticalSection

		内核态：api层面不去解决，丢给内核去解决，由用户态切换到内核态，又从内核态切换到用户态
			内核态的解决方案：
				事件：Event
					1. 定义事件  HANDLE
					2. 创建事件  CreateEvent
					3. 等待事件发生 WaitForSingleObject
					4. 取消事件  ResetEvent
					5. 产生事件  SetEvent
				互斥：
					1. 创建互斥   CreateMutex
					2. 产生一个通知
						等待通知到来

						释放通知

					互斥量 搭配临界区 或者搭配条件变量来使用

				信号量（旗语）：	semphore
					定义信号量			HANDLE
					创建信号量			CreateSemaphore
					等待操作			WaitForSingleObject
					设置信号量			ReleaseSemaphore

					操作系统内核 维护了一个变量   sem
						WaitForSingleObject 是去减少这个sem 减少到0就不可再减了，就阻塞
						ReleaseSemaphore	就是去增加
*/			



struct Student
{
	char name[20];
	int age;
	double score;
};


#if 0
void func()
{
	//线程2		每隔一秒输出一个递减的数字：
	int m = 0;
	while (1)
	{
		printf("m:%d\n", m--);
		Sleep(1000);
	}
}

#else
DWORD WINAPI func(LPVOID lpThreadParameter)
{
	/*
	//线程2		每隔一秒输出一个递减的数字：
	int m = (int)lpThreadParameter;
	while (1)
	{
		printf("m:%d\n", m--);
		Sleep(1000);
	}
	*/

	struct Student* p = (struct Student* )lpThreadParameter;
	while (1)
	{
		printf("%s	%d	%lf\n", p->name, p->age, p->score);
		Sleep(1000);

	}
}
#endif
int fun1(int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("i:%d\n", i);
		Sleep(1000);
	}
	return 2 * n;
}


int funv(void* p)
{
	for (int i = 0; i < 6; i++)
	{
		printf("i:%d\n", i);
		Sleep(1000);
	}
	return (int)((struct Student*)p + 2);
}

int main()
{
#if 0
	Student s[3] = {
		{"李飞",20,66.66},
		{"李淇",10,55.55},
		{"李飞",19,44.44},
					};

	//创建线程函数:
	CreateThread(NULL, NULL,
		/*(LPTHREAD_START_ROUTINE)func, */func,
		/*NULL,*/	/*(LPVOID)888,*/(LPVOID)(s+1),		//传参
		NULL, NULL);

#endif
#if 0
	//创建线程函数:
	HANDLE h1 = CreateThread(NULL, NULL,
		(LPTHREAD_START_ROUTINE)fun1,
		(LPVOID)6,	//传参
		NULL, NULL);
#endif

	Student s[3] = {
		{"李飞",20,66.66},
		{"李淇",10,55.55},
		{"李",19,44.44},
	};

	//创建线程函数:
	HANDLE h1 = CreateThread(NULL, NULL,
		(LPTHREAD_START_ROUTINE)funv,
		(LPVOID)s,	//传参
		NULL, NULL);

   //线程1		每隔一秒输出一个递增的数字：
	int n = 0;
	while (1)
	{
		printf("n:%d\n", n++);
		if (n > 3)
			break;

		Sleep(1000);
	}




	printf("开始的等待\n");
	WaitForSingleObject(h1, INFINITE);	//其实也是一个阻塞函数	要等到h1结束 才返回
	printf("等到了\n");

	/*
	DWORD ret = 66;
	BOOL isGet = GetExitCodeThread(h1, &ret);
	if (isGet)
		printf("ret: %d\n", ret);		//ret: 12
	
	*/
	DWORD ret = 66;
	BOOL isGet = GetExitCodeThread(h1, &ret);
			//printf("%d\n", isGet);
	struct Student* p = (struct Student*)ret;
	if (isGet)
		printf("%s	%d	%lf", p->name, p->age, p->score);
		//printf("ret: %d\n", ret);		

		

	return 0;
}


